Like any other programming language, JavaScript has its own list of best practices to make programs easier to read and maintain. There are a lot of tricky parts to JavaScript, so there are things we should avoid that reduce the quality of our code. By following best practices, we can create elegant and manageable code that’s easy for anyone to work with.
In this article, we’ll look at ways to improve the performance of our apps. Actions include caching data in variables, using the fastest way to loop through variables, reducing DOM access and elements on the page, and deferring script loading.
Reduce Access to Variables and Properties
We should reduce the times that we access variables and object properties in our apps.
This is because every time we do this, the CPU has to access the item in memory again and again to compute its results.
Therefore, we should do this as little as possible.
For example, if we have a loop, we shouldn’t write the following:
for (let i = 0; i < arr.length; i++) {
}
Instead, we should write:
let length = arr.length;
for (let i = 0; i < length; i++) {
}
This way, arr.length
is only referenced once in our loop instead of accessing it in every iteration.
Fastest Way to Loop Through Variables
In JavaScript, there’re multiple ways to iterate through iterable objects. One is the good old for
loop. Other ways include the for...of
loop, the forEach
method for arrays. map
and filter
also loop through the array as the map and filter operations are being done. There’s also the while
loop.
Of all the ways to run loops, the for
loop is the fastest way, with or without caching the length
like we did above. Caching the length
sometimes makes the loop performs better, however.
Some browser engines have optimized the for
loop without caching the length property.
The while
loop with decrementing index is approximately 1.5 times slower than the for
loop
Using the forEach
loop is 10 times slower than the for
loop, so it’s probably better to avoid it, especially for large arrays.
We can see the results here.
Reduce DOM Access
Accessing the DOM is an expensive operation since the browser has to grab the element from the web page and then create an object from it and return it.
To reduce DOM access, we should set the DOM Node objects to a variable if we need to manipulate it more than once.
For example, if we have the following HTML and we want to set some text to it after a few seconds:
<p id='foo'>
</p>
We can write the following code to do so:
const setText = (element, textContent) => {
return new Promise((resolve) => {
setTimeout(() => {
element.textContent = textContent;
resolve();
}, 3000)
})
}
(async () => {
const foo = document.querySelector('#foo');
await setText(foo, 'foo');
await setText(foo, 'bar');
await setText(foo, 'baz');
})();
In the code above, we have one function which gets the HTML element that we want to manipulate, and the text content that we want to set.
The setText
function returns a promise to set the text to a given element after 3 seconds.
Then we have an async
function to set the text 3 times. The important part is that we pass in the reference to the element in each call. This way we don’t have to get the element from the web page 3 times, which is an expensive operation.
Photo by Sawyer Bengtson on Unsplash
Reduce DOM Size
Rendering the DOM tree is slow. Therefore, we have to reduce the size of the tree.
There’s no way around it but to make our web pages as simple as possible.
Having a smaller DOM make searching for elements with methods like querySelector
, getElementById
, or getElementsByTagName
faster since there’s less to look for.
Also, page rendering performance will improve since there’s less to load. This is especially true for slower devices like phones and tablets.
Don’t Declare Unnecessary Variables
Every time we declare variables, the browser has to reserve memory space for the variables. Therefore, to reduce memory usage, we should declare too many variables.
For example, if we have the following HTML:
<div id='foo'>
<p>
</p>
</div>
And we want to set the text content of the p
element, we shouldn’t write something like:
const foo = document.querySelector('#foo');
const p = foo.querySelector('p');
p.textContent = 'foo';
since we have 2 variables. This means our computer has to store the values for 2 more JavaScript variables.
Instead, we can reduce to no variable declarations by writing:
document.querySelector('#foo p').textContent = 'foo';
As we can see, we can use the querySelector
method to select anything with CSS selectors. This means that we should use this method and the related querySelectorAll
method to select elements since they can both use CSS selectors to select any HTML element node.
Defer the Loading of Scripts
Loading JavaScript files is an expensive operation. The browser has to download the file, parse the content, and then convert it to machine code and run it.
The browser will download one file at a timeline by line, so it’ll hold up any other operation from happening.
Therefore, we should delay it as much as we can. We can do this by putting the script
tag to the end. Also, we can use the defer
attribute on the script
tag to do this.
Also, we can run scripts after the page is loaded by create script
elements on the fly and appending it as follows:
window.onload = () => {
const element = document.createElement("script");
element.src = "https://code.jquery.com/jquery-1.12.4.min.js";
document.body.appendChild(element);
};
Anything that can be loaded after the page loads can use this method of script loading.
We can speed up our pages by doing a few things. First, we can cache data in variables so we don’t have to access them repeatedly. Then we can loop through items faster with the for
loop.
Also, we can reduce the DOM size to reduce the items that need to be loaded. We can also cache DOM objects in our code by assigning them to variables.
We also should not declare unnecessary variables, and we should defer the loading of scripts as much as possible so it won’t hold up our browser.